賀!此系列文榮獲 2023 iThome 鐵人賽《優選》獎項,正在規劃出書中,感謝大家的支持🙏,同名課程「Java 工程師必備!Spring Boot 零基礎入門」也已在 Hahow 平台上架
哈囉大家好,我是古古
在上一篇文章中,我們已經了解了 Spring AOP 的概念和原理,那麼這篇文章,我們就會接著來介紹,要如何在 Spring Boot 中使用 Spring AOP 的功能
在上一篇文章中有提到,AOP 的全稱是 Aspect-Oriented Programming,中文是翻譯為「切面導向程式設計」或是「剖面導向程式設計」,而 AOP 的概念,就是「透過切面,統一的去處理方法之間的共同邏輯」
因此當我們使用了 AOP 之後,就再也不用去複製貼上程式了,我們只需要在切面裡面寫好測量時間的程式,之後就可以在任何地方去使用這個切面,讓這個切面替我們完成測量時間的功能了
如果想要在 Spring Boot 中使用 Spring AOP 的功能的話,首先會需要在 pom.xml 這個檔案裡面新增以下的程式,將 Spring AOP 的功能給載入進來
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
加完上述的程式之後,在 pom.xml 的右上角會出現一個 M 符號,點擊這個 M 符號,就可以更新這個 Spring Boot 程式,把 Spring AOP 的功能給載入進來
加好 Spring AOP 的功能之後,我們就可以來使用 Spring AOP 的專屬註解了!
如果我們想要使用 Spring AOP,去創造一個新的切面出來的話,那我們就可以在 class 上面加上 @Aspect
,這樣子就可以成功創建一個切面出來了
譬如說我們可以先創建一個新的 class 叫做 MyAspect,然後在上面加上 @Aspect
,這樣就可以將 MyAspect 變成是一個切面了
不過在使用 @Aspect
去創建新切面時,一定要特別注意,只有 Bean 才可以變成一個切面,所以換句話說的話,就是在使用 @Aspect
時,也要同時使用 @Component
,將該 class 變成 Bean 的同時,切面的設定才會生效,如果只有在 class 上面加上 @Aspect
的話,是完全沒有任何效果的!
反正無論如何,大家只要記得,在使用切面時,需要「@Component
和 @Aspect
要一起使用」就對了
創建好切面 MyAspect 這個 class 之後,我們就可以在這個 class 裡面去撰寫切面方法,並且指定要在什麼時機點,去切入方法了
舉例來說,我們可以在 MyAspect class 裡面,先寫上一個 before() 方法,然後在這個 before() 方法裡面,輸出一行「I'm before」的訊息到 console 上
接著,只要我們在這個 before() 方法的上面,去加上一個 @Before
,並且在後面指定想要的切入點,就可以在這個切入點的方法 「執行前」,去執行這個 MyAspect class 中的 before() 方法了
那上面那樣的講法可能有點抽象,所以我們可以再試著來拆解一下這段程式,了解怎麼解讀這些 AOP 的程式
到目前為止,我們有在 MyAspect class 裡面寫上了一個 before() 方法,並且在他上面去加上了一個 @Before
,那這一段程式是可以拆成三個步驟來解讀的:
在 @Before
後面的小括號中的程式,稱為「切入點 (Pointcut)」,即是去指定哪個方法要被切
舉例來說,假設我們想要測量的是 HpPrinter 中的方法的時間,那麼切面就是測量時間的程式,而 HpPrinter 中的方法,就是切入點 (Pointcut)
所以在 @Before
後面的小括號中的程式,即是去指定「切入點 (Pointcut)」,表示我們想要讓哪個方法,最後會被 MyAspect 這個切面所切
確認好了切入點之後,接著就是查看前面所加上的註解是什麼,像是這邊所加上的,就是 @Before
,而 @Before
的用途,就是指定要在小括號中的切入點 「執行前」,去執行下面的 before() 方法
所以簡單的說的話,前面的這個 @Before
,他指定的就是 「時機點」,@Before
對應的就是切入點方法「執行前」執行,AOP 還有提供其他時機點的不同註解,像是 @After
和 @Around
,後續也會再跟大家做介紹
因此在步驟二這裡,就是去確認「切面方法執行的時機點」
當我們確認好「切入點」和「時機點」之後,最後就是在下面的 before() 方法中,去撰寫切面的程式
像是在這裡我們就只在 before() 方法裡面寫上一行程式,去輸出「I'm before」的資訊到 console 上
所以綜合上述的三個步驟,就可以去解讀這一段 AOP 的程式的含義是什麼了
因此最後的結果,就會長的像是下圖這樣:
看完了上述對 @Aspect
和 @Before
的介紹,我們也可以直接到 Spring Boot 程式中添加這些程式,實際的感受一下,Spring AOP 這個切面的邏輯到底是怎麼個運作法
首先我們可以把之前的 HpPrinter 給刪減一下,將 count 變數的相關程式刪掉,只留下輸出「HP 印表機: xxx」的程式即可
@Component
public class HpPrinter implements Printer {
@Override
public void print(String message) {
System.out.println("HP 印表機: " + message);
}
}
接著同樣是在 com.example.demo 這個 package 底下,創建一個新的 MyAspect class 出來,並且添加下列的程式
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.example.demo.HpPrinter.*(..))")
public void before() {
System.out.println("I'm before");
}
}
接著只需要確認 MyController 中有成功注入 HpPrinter 進來,並且有去呼叫到 print() 方法,這樣就可以去運行 Spring Boot 程式了
@RestController
public class MyController {
@Autowired
private Printer printer;
@RequestMapping("/test")
public String test() {
printer.print("Hello World");
return "Hello World";
}
}
當 Spring Boot 程式運行成功之後,大家可以訪問 http://localhost:8080/test ,理論上會出現和之前的文章中的運行結果一樣,都是在瀏覽器中出現一個「Hello World」的字串
而在 IntelliJ 的 console 中,則可以看到出現了兩行字,分別是「I'm before」以及「HP 印表機: Hello World」,所以這就表示 MyAspect 中的 before() 切面方法,就確實的在 HpPrinter 的 print() 方法前執行了,這就是 Spring AOP 幫助我們做到的
因此透過這個例子,大家就可以體會到 Spring AOP 的強大功能,當有了 Spring AOP 之後,我們就不需要將所有程式通通都塞到 print() 方法裡面,而是可以將共用的部分拆分出來,統一的寫在切面中進行管理
所以以後當哪裡有需要這個共同邏輯,我們就可以去重複使用這個切面,這樣子不僅可以達到程式重複利用的好處,也可以讓各個 class 更專注的在處理他的功能,而不是添加一堆其他程式了
在 Spring AOP 中,除了可以使用 @Before
達到在方法「執行前」執行切面之外,我們也是可以將 @Before
替換成 @After
或是 @Around
,在不同的時機點去執行切面的
在 Spring AOP 裡面,有三種時機點可以選擇:
@Before
:在方法「執行前」執行切面@After
:在方法「執行後」執行切面@Around
:在方法「執行前」和「執行後」,執行切面@After
的寫法其實和 @Before
一模一樣,只要把 @Before
換成 @After
就可以用了,所以實際使用起來會是下面這個樣子
@Aspect
@Component
public class MyAspect {
@After("execution(* com.example.demo.HpPrinter.*(..))")
public void after() {
System.out.println("I'm after");
}
}
@Around
因為寫起來比較複雜,因此此處提供程式給大家參考
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.example.demo.HpPrinter.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("I'm around before");
// 執行切入點的方法,obj 為切入點方法執行的結果
Object obj = pjp.proceed();
System.out.println("I'm around after");
return obj;
}
}
在前面的程式中,我們有在 @Before
後面加上一段看起來很長的程式,那一段程式就是在指定方法的切入點為何
切入點的寫法是有一定規則寫法的,像是上面的這段程式,就是在表示切入點為「HpPrinter 的所有方法」
不過由於這個切入點寫起來還滿複雜的,而且使用頻率也不是很高,因此就建議大家有用到的時候再去查詢就可以了,以下提供幾種常見的寫法邏輯給大家:
execution(* com.example.demo.HpPrinter.print())
execution(* com.example.demo.HpPrinter.*(..))
execution(* com.example.demo.*(..))
execution(* com.example.demo..*(..))
@MyAnnotation
的方法@annotation(com.example.demo.MyAnnotation)
了解了 Spring AOP 的用法之後,最後跟大家介紹一下 Spring AOP 的相關發展
Spring AOP 以前最常被用在以下三個地方:
但是由於 Spring Boot 發展逐漸成熟,因此上述這些功能,都已經被封裝成更好用的工具讓我們使用了,所以大家目前已經比較少直接使用 @Aspect
去創建一個切面出來了
舉例來說,像是權限驗證這一塊,我們就會改成使用 Spring Security 這個工具來完成,不過 Spring Security 的底層仍舊是透過 Spring AOP 來完成的,只是 Spring Security 把他封裝得更好、使用上更方便,因此在進行權限驗證時,使用 Spring Security 是更能提升大家開發的效率的
所以雖然 Spring AOP 已經漸漸淡出大家的日常使用,不過他作為 Spring 框架中的重要特性之一,還是常常生活在我們周邊的,只是我們可能感覺不太到而已XD
因此上述所介紹的 Spring AOP 的相關用法,大家就有個印象就可以了,重點是要把 AOP 的切面概念搞懂,至於其他的 @Before
、@After
、@Around
的用法,就有個印象就可以了
這篇文章介紹了要如何透過 @Aspect
和 @Before
,使用切面去切入指定的切入點,在 Spring Boot 中使用 Spring AOP 的功能,並且也簡單介紹了另外兩個時機點的註解 @After
和 @Around
,最後也補充了切入點的撰寫方式,以及 Spring AOP 的相關發展
那麼有關 Spring AOP 的介紹就到這邊結束了,Spring AOP 作為 Spring 框架中的重要特性之一,縱使他已經在日常開發中很少用到,但是 AOP 切面的概念,仍舊是在許多功能中被廣泛應用的
那所以下一篇文章,我們就會進入到下一個章節:Spring MVC,來了解要如何在 Spring Boot 中和「前端」進行溝通,那我們就下一篇文章見啦!